package com.evil.kingdom.service.person.king.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.evil.common.core.util.StreamUtil;
import com.evil.kingdom.pojo.dto.person.base.PersonDto;
import com.evil.kingdom.pojo.dto.person.general.GeneralDto;
import com.evil.kingdom.pojo.dto.person.king.KingDto;
import com.evil.kingdom.pojo.dto.person.soldier.SoldierDto;
import com.evil.kingdom.pojo.entity.person.base.Person;
import com.evil.kingdom.pojo.entity.person.king.King;
import com.evil.kingdom.pojo.enums.PersonTypeEnum;
import com.evil.common.mongo.service.impl.SingleServiceImpl;
import com.evil.kingdom.repository.person.king.KingRepository;
import com.evil.kingdom.service.person.base.PersonService;
import com.evil.kingdom.service.person.general.GeneralService;
import com.evil.kingdom.service.person.king.KingService;
import com.evil.kingdom.service.person.soldier.SoldierService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 君主
 *
 * @author liyang
 * @date 2024-05-22 16:57
 */
@Slf4j
@Service
@AllArgsConstructor
public class KingServiceImpl extends SingleServiceImpl<KingRepository, King> implements KingService {

    private final PersonService personService;
    private final SoldierService soldierService;
    private final GeneralService generalService;

    @Override
    public PersonTypeEnum type() {
        return PersonTypeEnum.KING;
    }

    @Override
    public Optional<KingDto> isExistDtoByName(String name) {
        return personService.isExistByName(name).flatMap(this::isExistDtoByPerson);
    }

    @Override
    public KingDto findDtoByName(String name) {
        return this.findDtoByPerson(personService.findByName(name));
    }

    @Override
    public List<KingDto> isExistDtoListByNames(Collection<String> names) {
        if (CollectionUtil.isEmpty(names)) {
            return new ArrayList<>();
        }
        List<PersonDto> persons = personService.findDtoByNames(names);
        Map<String, PersonDto> personMap = StreamUtil.toMapK(persons, PersonDto::getName);

        List<King> kings = this.isExistByNames(personMap.keySet());
        return this.toDto(personMap, kings, soldierService::isExistDtoListByNames, generalService::isExistDtoListByNames);
    }

    @Override
    public List<KingDto> findDtoListByNames(Collection<String> names) {
        if (CollectionUtil.isEmpty(names)) {
            return new ArrayList<>();
        }
        List<PersonDto> persons = personService.findDtoByNames(names);
        Map<String, PersonDto> personMap = StreamUtil.toMapK(persons, PersonDto::getName);

        List<King> kings = this.findByNames(personMap.keySet());
        return this.toDto(personMap, kings, soldierService::findDtoListByNames, generalService::findDtoListByNames);
    }

    @Override
    public Optional<KingDto> isExistDtoByPerson(Person person) {
        return this.isExistDtoByPersonDto(personService.findDtoByPerson(person));
    }

    @Override
    public KingDto findDtoByPerson(Person person) {
        return this.findDtoByPersonDto(personService.findDtoByPerson(person));
    }

    @Override
    public Optional<KingDto> isExistDtoByPersonDto(PersonDto personDto) {
        return this.isExistByName(personDto.getName()).map(e -> {
            List<SoldierDto> soldierSet = soldierService.isExistDtoListByNames(e.getSoldiers());
            List<GeneralDto> generalSet = generalService.findDtoListByNames(e.getGenerals());
            return this.toDto(personDto, e, new HashSet<>(soldierSet), new HashSet<>(generalSet));
        });
    }

    @Override
    public KingDto findDtoByPersonDto(PersonDto personDto) {
        King king = this.findByName(personDto.getName());
        List<SoldierDto> soldierSet = soldierService.isExistDtoListByNames(king.getSoldiers());
        List<GeneralDto> generalSet = generalService.isExistDtoListByNames(king.getSoldiers());
        return this.toDto(personDto, king, new HashSet<>(soldierSet), new HashSet<>(generalSet));
    }

    private List<KingDto> toDto(Map<String, PersonDto> personMap, Collection<King> kings,
                                Function<Collection<String>, Collection<SoldierDto>> querySoldier,
                                Function<Collection<String>, Collection<GeneralDto>> queryGeneral) {
        // 君主 士兵map
        Map<String, Set<String>> kingSoldiersMap = new HashMap<>();
        // 君主 武将map
        Map<String, Set<String>> kingGeneralsMap = new HashMap<>();
        // 所有士兵
        HashSet<String> soldierNames = new HashSet<>();
        // 所有武将
        HashSet<String> generalNames = new HashSet<>();
        kings.forEach(e -> {
            kingSoldiersMap.put(e.getName(), e.getSoldiers());
            kingGeneralsMap.put(e.getName(), e.getGenerals());
            soldierNames.addAll(e.getGenerals());
            generalNames.addAll(e.getGenerals());
        });
        // 获取士兵
        Collection<SoldierDto> soldiers = querySoldier.apply(soldierNames);
        Map<String, SoldierDto> soldierMap = StreamUtil.toMapK(soldiers, SoldierDto::getName);
        // 获取武将
        Collection<GeneralDto> generals = queryGeneral.apply(generalNames);
        Map<String, GeneralDto> generalMap = StreamUtil.toMapK(generals, GeneralDto::getName);
        // 返回
        return StreamUtil.transListT(kings, king -> {
            // 当前君主士兵
            Set<SoldierDto> soldierSet = kingSoldiersMap.get(king.getName()).stream()
                    .filter(soldierMap::containsKey)
                    .map(soldierMap::get).collect(Collectors.toSet());
            // 当前武将士兵
            Set<GeneralDto> generalSet = kingGeneralsMap.get(king.getName()).stream()
                    .filter(generalMap::containsKey)
                    .map(generalMap::get).collect(Collectors.toSet());
            return this.toDto(personMap.get(king.getName()), king, soldierSet, generalSet);
        });
    }

    private KingDto toDto(PersonDto person, King king, Set<SoldierDto> soldierSet, Set<GeneralDto> generalSet) {
        KingDto kingDto = BeanUtil.copyProperties(person, KingDto.class);
        BeanUtil.copyProperties(king, kingDto);
        kingDto.setSoldierSet(soldierSet);
        kingDto.setGeneralSet(generalSet);
        return kingDto;
    }
}
